/**   
 * Copyright 2011 COMERE Harold
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package etrl.core;

import etrl.vecmath.AxisAngle4f;
import etrl.vecmath.Matrix4f;
import etrl.vecmath.Quat4f;
import etrl.vecmath.Vector3f;

/**
 * \class TransformData
 * \brief Data storage class representing a position and orientation
 */
public class TransformData
{
	/**
	 * Quaternion storing current orientation
	 */
	private Quat4f _orientation;

	/**
	 * Current position
	 */
	private Vector3f _position;

	/**
	 * Scale applied
	 */
	private Vector3f _scale;

	/**
	 * \brief Initializes an identity transform data. 
	 */
	public TransformData()
	{
		_position = new Vector3f(0.f, 0.f, 0.f);
		_orientation = new Quat4f(0.f, 0.f, 0.f, 1.f);
		_scale = new Vector3f(1.0f, 1.0f, 1.0f);
	}
	
	/**
	 * \brief Copy constructor.
	 * @param[in] t Transform to copy.
	 */
	public TransformData(TransformData t) {
		_position = new Vector3f(t.getPosition());
		_orientation = new Quat4f(t.getOrientation());
		_scale = t.getScale();
	}

	/**
	 * \brief Clones the transform data.
	 * @return cloned TransformData.
	 */
	public TransformData clone()
	{
		TransformData transformData = new TransformData();
		transformData.setPosition(_position);
		transformData.setOrientation(_orientation);
		transformData.setScale(_scale);
		return transformData;
	}
	
	/**
	 * Applies another transform data to this transform data ...
	 * @param[in] transformData transform data to apply.
	 */
	public void apply(TransformData transformData)
	{
		_position.add(transformData.getPosition());
		
		_scale.x *= transformData.getScale().x;
		_scale.y *= transformData.getScale().y;
		_scale.z *= transformData.getScale().z;

		_orientation.mul(transformData.getOrientation());
	}
	
	/**
	 * \brief Applies a translation to the position.
	 * @param[in] translationVector translation vector to apply.
	 */
	public void translate(Vector3f translationVector) 
	{
		_position.add(translationVector);
	}

	/**
	 * \brief Sets a new position.
	 * @param[in] newPosition position to set.
	 */
	public void setPosition(Vector3f newPosition) 
	{
		_position.set(newPosition);
	}

	/**
	 * \brief Sets a new orientation.
	 * @param[in] newOrientation
	 */
	public void setOrientation(Quat4f newOrientation) {
		_orientation.set(newOrientation);
	}

	/**
	 * \brief Applies a rotation defined by an axis and an angle in degrees
	 * @param[in] axis rotation axis
	 * @param[in] degrees rotation angle in degrees
	 * @param[in] local true : do local rotation, false : do global rotation
	 */
	public void applyRotation(Vector3f axis, float degrees, boolean local) 
	{
		// Compute the rotation quaternion
		Quat4f qrot = new Quat4f();
		qrot.set(new AxisAngle4f(axis, degrees * (float) Math.PI / 180.f));

		// Apply the rotation in local or global space
		if (local)
			_orientation.mul(qrot);
		else
			_orientation.mul(qrot, _orientation);

		// Then normalize the the rotation result
		_orientation.normalize();
	}

	/**
	 * \brief Computes the matrix used to go from world space to transform local space.
	 * @return World to local space matrix.
	 */
	public Matrix4f toMat4() 
	{
		return new Matrix4f(_orientation, _position, _scale);
	}

	/**
	 * \brief Gets the transform local x axis.
	 * @return local x axis
	 */
	public Vector3f getLocalXAxis()
	{
		Matrix4f mat = toMat4();
		return new Vector3f(mat.getM00(), mat.getM10(), mat.getM20());
	}

	/**
	 * \brief Gets the transform local y axis
	 * @return local y axis
	 */
	public Vector3f getLocalYAxis() 
	{
		Matrix4f mat = toMat4();
		return new Vector3f(mat.getM01(), mat.getM11(), mat.getM21());
	}

	/**
	 * \brief Gets the transform local z axis
	 * @return local z axis
	 */
	public Vector3f getLocalZAxis() 
	{
		Matrix4f mat = toMat4();
		return new Vector3f(mat.getM02(), mat.getM12(), mat.getM22());
	}

	/**
	 * \brief Applies a translation on the local Z axis about unity * multiplier unit
	 * @param[in] multiplier multiplier of unit axis
	 */
	public void translateOnZAxis(float multiplier) {
		Vector3f zaxis = getLocalZAxis();
		zaxis.scale(multiplier);
		translate(zaxis);
	}

	/**
	 * \brief Applies a translation on the local X axis about unity * multiplier unit
	 * @param[in] multiplier multiplier of unit axis
	 */
	public void translateOnXAxis(float multiplier) {
		Vector3f xaxis = getLocalXAxis();
		xaxis.scale(multiplier);
		translate(xaxis);
	}

	/**
	 * \brief Applies a translation on the local Y axis about unity * multiplier unit
	 * @param[in] multiplier multiplier of unit axis
	 */
	public void translateOnYAxis(float multiplier) {
		Vector3f yaxis = getLocalYAxis();
		yaxis.scale(multiplier);
		translate(yaxis);
	}
	
	/**
	 * \brief Applies an uniform scale
	 * @param[in] uniformScale uniform scale value
	 */
	public void setUniformScale(float uniformScale) 
	{
		_scale.x = uniformScale;
		_scale.y = uniformScale;
		_scale.z = uniformScale;
	}
	
	/**
	 * \brief Applies a given scale
	 * @param[in] scale scale to apply
	 */
	public void setScale(final Vector3f scale)
	{
		_scale.x = scale.x;
		_scale.y = scale.y;
		_scale.z = scale.z;
	}

	/**
	 * \brief Gets the current position.
	 * @return Current position.
	 */
	public Vector3f getPosition() {
		return _position;
	}

	/**
	 * \brief Gets the current scale.
	 * @return Transform scale.
	 */
	public Vector3f getScale() 
	{
		return _scale;
	}

	/**
	 * \brief Gets the current orientation
	 * @return Transform orientation.
	 */
	public Quat4f getOrientation() {
		return _orientation;
	}
}
